﻿using Motoball.screens;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;



namespace Motoball.screenmanager
{
    public class DrawSet
    {
        public bool bloomEnabled = false;
        public static GraphicsDevice device;
        string id;
        public float bloomIntensity;
        public float baseIntensity;
        public ResolveTexture2D texture;
        
        public DrawSet(string id, float bloomIntensity, float baseIntensity)
        {
            this.id = id;
            this.bloomIntensity = bloomIntensity;
            this.baseIntensity = baseIntensity;

            texture = new ResolveTexture2D(device,
                device.PresentationParameters.BackBufferWidth,
                device.PresentationParameters.BackBufferHeight,
                1,
                device.PresentationParameters.BackBufferFormat);
        }
        public String Id
        {
            get { return id; }
        }
        public void SetBloomValues(float value)
        {
            bloomIntensity = baseIntensity = value;
        }
        public void SetGlobalBloom()
        {
            ContentStorage.bloomIntensity = bloomIntensity;
            ContentStorage.baseIntensity = baseIntensity;
        }

    }
    public class ScreenManager : DrawableGameComponent
    {
        public string currentDrawSetId;
        
        public List<DrawSet> drawSets;
        Motoball game;
        List<GameScreen> screens;
        //temporary list of all screens which need to be updated/drawn
        List<GameScreen> toUpdate;
        SpriteBatch spriteBatch;
        public SpriteFont debugFont;
        ContentManager content;
        public GameplayScreen gameplayPointer;
        //memo: try to find a more sophisticated solution...
        public MenuTree gameplayDummy;

        List<GameScreen> totalScreens;

        MenuTree mainTree;
        MenuTree currentTree;

        //CountdownScreen countdownScreen;
        //GameplayScreen gameplayScreen;
        //MainMenuScreen mainMenu;
        //MaxScoreScreen maxScoreScreen;
        //MultiplayerScreen multiplayerScreen;
        //PaddleSelectionScreen paddleSelection;
        //PauseScreen pauseScreen;
        //RingSizeSelectionScreen ringSizeSelectionScreen;
        //SingleplayerScreen singleplayerScreen;
        //WinningScreen winningScreen;

        public ScreenManager(Game game):base(game)
        {
            this.game = (Motoball)game;
            content = game.Content;
            
        }
       
        //private SpriteFont debugFont;
        //double[] averageFPS;
        //double lowestFPS = 100.0;
        //float deltaFPSTime = 0;
        //float memory = 0;
        //int fpsCount = 0;

        #region Methods

        public bool RemoveScreen(GameScreen screenToRemove)
        {
            return screens.Remove(screenToRemove);
        }
        public void AddScreen(GameScreen screen)
        {
            screens.Add(screen);
            if (screen != null)
            {
                screen.SetToActive(); 
                screen.Reset();
            }
        }
        public override void Initialize()
        {
            screens = new List<GameScreen>();
            toUpdate = new List<GameScreen>();

            //add all screens to list
            totalScreens = new List<GameScreen>(10);
            //totalScreens.Add(new PresentationScreen("presentationScreen"));
            totalScreens.Add(new ArcadeModeScreen("arcadeModeScreen"));
            totalScreens.Add(new CameraMovementScreen("cameraMovementScreen"));
            totalScreens.Add(new CountdownScreen("countdownScreen"));
            
            
            totalScreens.Add(new InstantMatchScreen("instantMatchScreen"));
            totalScreens.Add(new LongBallScreen("longBallScreen"));
            totalScreens.Add(new MainMenuScreen("mainMenuScreen"));
            totalScreens.Add(new MaxScoreScreen("maxScoreScreen"));
            totalScreens.Add(new MultiplayerScreen("multiplayerScreen"));
            totalScreens.Add(new MultiplayerGameplayScreen("multiplayerGameplayScreen"));
            totalScreens.Add(new PaddleSelectionScreen("paddleSelectionScreen"));
            totalScreens.Add(new PauseScreen("pauseScreen"));
            totalScreens.Add(new ResetProfileScreen("resetProfileScreen"));
            totalScreens.Add(new RingSizeSelectionScreen("ringSizeSelectionScreen"));
            totalScreens.Add(new SingleplayerScreen("singleplayerScreen"));
            totalScreens.Add(new SingleplayerGameplayScreen("singleplayerGameplayScreen"));
            totalScreens.Add(new SurvivialModeScreen("survivialModeScreen"));
            totalScreens.Add(new TournamentScreen("tournamentScreen"));
            totalScreens.Add(new WinningScreen("winningScreen"));

            totalScreens.Add(new SplashScreen("creditsScreen"));
            totalScreens.Add(new SplashScreen("instructionsScreen"));


            GameScreen.screenManager = this;
            gameplayPointer = (GameplayScreen)GetScreen("singleplayerGameplayScreen");

            InitializeMenuTree();

            base.Initialize();
        }
        private float bloomBase = 4.5f;
        protected override void LoadContent()
        {
            base.LoadContent();
           
            spriteBatch = new SpriteBatch(GraphicsDevice);
            debugFont = content.Load<SpriteFont>("Fonts\\PointsFont");
           drawSets = new List<DrawSet>();
            DrawSet.device = GraphicsDevice;
            drawSets.Add(new DrawSet("default", 1f, 1f));
            drawSets.Add(new DrawSet("defaultBloom", bloomBase, bloomBase));
            GetDrawSet("defaultBloom").bloomEnabled = true;
            //drawSets.Add(new DrawSet("chargeBloom0", 1f, 1f));
            //GetDrawSet("chargeBloom0").bloomEnabled = true;
            //drawSets.Add(new DrawSet("chargeBloom1", 1f, 1f));
            //GetDrawSet("chargeBloom1").bloomEnabled = true;
            drawSets.Add(new DrawSet("foreground", 1f, 1f));
            
            
            
            //debugFont = content.Load<SpriteFont>("Fonts\\PointsFont");
            //averageFPS = new double[100];

            foreach (GameScreen screen in totalScreens)
            {
                screen.Initialize();
                screen.LoadContent();
            }
            SplashScreen splash = (SplashScreen)GetScreen("instructionsScreen");
            splash.Background = content.Load<Texture2D>("Images\\instructions");
            splash.backgroundColor = Color.Black;
            splash = (SplashScreen)GetScreen("creditsScreen");
            splash.Background = content.Load<Texture2D>("Images\\Credits");
            //splash.backgroundColor.A = 100;

            //modelSplash.LoadContent(content.Load<Model>("Models\\Exit_Button"));
            
            //add first screen to active-screens-list
            AddScreen(currentTree.item);

            

            //drawSets[0].texture = new ResolveTexture2D(GraphicsDevice,
            //    ContentStorage.screenWidth,
            //    ContentStorage.screenHeight,
            //    0,
            //    SurfaceFormat.Bgr32);
            Paddles.Paddle.input = Input;
            
        }

        public void AddDrawSet(string id)
        {
            if (GetDrawSet(id) == null)
                drawSets.Insert(drawSets.Count - 1, new DrawSet(id, 1f, 1f));
            else
                return;
        }
        public void RemoveDrawSet(string id)
        {
            DrawSet toDelete = null;
            foreach (DrawSet set in drawSets)
            {
                if (set.Id == id)
                {
                    toDelete = set;
                    break;
                }
            }
            if (toDelete != null)
                drawSets.Remove(toDelete);
        }
        public DrawSet GetDrawSet(string id)
        {
            foreach (DrawSet set in drawSets)
                if (set.Id == id)
                    return set;

            return null;
        }

        /// <summary>
        /// builds all menu-relations
        /// </summary>
        private void InitializeMenuTree()
        {
            mainTree = new MenuTree(GetScreen("mainMenuScreen"));
            //mainTree = new MenuTree(GetScreen("presentationScreen"));
            mainTree.AddItem(GetScreen("paddleSelectionScreen"));
            mainTree.AddItem(GetScreen("paddleSelectionScreen"));
            mainTree.AddItem(GetScreen("creditsScreen"));
            mainTree.AddItem(GetScreen("instructionsScreen"));
            //mainTree.AddItem(GetScreen("singleplayerScreen"));
            //mainTree.AddItem(GetScreen("multiplayerScreen"));
            //mainTree.AddItem(GetScreen("creditsScreen"));
            //mainTree.AddItem(GetScreen("instructionsScreen"));
            //mainTree.AddItem(GetScreen("resetProfileScreen"));

            mainTree.childs.ElementAt(0).
                AddItem(GetScreen("paddleSelectionScreen")).
                AddItem(GetScreen("maxScoreScreen")).
                AddItem(GetScreen("gameplayScreen"));

            mainTree.childs.ElementAt(1).
                AddItem(GetScreen("paddleSelectionScreen")).
                AddItem(GetScreen("maxScoreScreen")).
                AddItem(GetScreen("gameplayScreen"));


            /*
            //singleplayer
            MenuTree singleplayer = mainTree.childs.ElementAt(0);
            singleplayer.AddItem(GetScreen("instantMatchScreen"));
            singleplayer.AddItem(GetScreen("arcardeModeScreen"));
            singleplayer.AddItem(GetScreen("survivalModeScreen"));

            //instant
            singleplayer.childs.ElementAt(0).
                AddItem(GetScreen("ringSizeSelectionScreen")).
                AddItem(GetScreen("paddleSelectionScreen")).
                AddItem(GetScreen("maxScoreScreen")).
                AddItem(GetScreen("gameplayScreen"));
            
            //arcade
            singleplayer.childs.ElementAt(1).
                AddItem(GetScreen("paddleSelectionScreen")).
                AddItem(GetScreen("gameplayScreen"));

            //survival
            singleplayer.childs.ElementAt(2).
                AddItem(GetScreen("paddleSelectionScreen")).
                AddItem(GetScreen("gameplayScreen"));

            //multiplayer
            MenuTree multiplayer = mainTree.childs.ElementAt(1);
            multiplayer.AddItem(GetScreen("instantMatchScreen"));
            multiplayer.AddItem(GetScreen("longBallScreen"));
            multiplayer.AddItem(GetScreen("TournamentScreen"));

            //instant
            multiplayer.childs.ElementAt(0).
                AddItem(GetScreen("ringSizeSelectionScreen")).
                AddItem(GetScreen("paddleSelectionScreen")).
                AddItem(GetScreen("maxScoreScreen")).
                AddItem(GetScreen("gameplayScreen"));

            //longBall
            multiplayer.childs.ElementAt(1).
                AddItem(GetScreen("ringSizeSelectionScreen")).
                AddItem(GetScreen("paddleSelectionScreen")).
                AddItem(GetScreen("gameplayScreen"));

            //tournament
            multiplayer.childs.ElementAt(2).
                AddItem(GetScreen("paddleSelectionScreen")).
                AddItem(GetScreen("gameplayScreen"));
            */

            //currentTree = mainTree;//.childs.ElementAt(1).childs.ElementAt(0).childs.ElementAt(0).childs.ElementAt(0).childs.ElementAt(0);
            currentTree = mainTree;//.childs.ElementAt(0).childs.ElementAt(0).childs.ElementAt(0);
        }
        public void ToPreviousMenu()
        {
            currentTree = currentTree.parentTree;
            if (currentTree == null)
                ExitGame();
            else
                AddScreen(currentTree.item);
        }
        public void ToNextMenu(int item)
        {
            try
            {
                if (currentTree.childs[0].Id == "singleplayerGameplayScreen")
                {
                    gameplayDummy = new MenuTree(gameplayPointer);
                    currentTree = gameplayDummy;
                }
                else
                {
                    if (currentTree.childs.Count <= item)
                        currentTree = currentTree.childs.ElementAt(0);
                    else
                        currentTree = currentTree.childs.ElementAt(item);
                }
            }
            catch (ArgumentOutOfRangeException e)
            {
                System.Console.Write(e.Message);
                currentTree = mainTree;
            }
            AddScreen(currentTree.item);
        }
        string debugString = "";
        public void AddDebugString(string text)
        {
            debugString += "\n" + text;
        }
        private void DrawAll(GameTime gameTime)
        {
            foreach (GameScreen screen in toUpdate)
            {
                screen.Draw(gameTime);
            }
        }
        
        int frameCounter = 0;
        float timeCounter = 0;
        float frames = 0;
        bool save = false;
        public override void Draw(GameTime gameTime)
        {
            if(Input.IsNewKeyPress(Microsoft.Xna.Framework.Input.Keys.M))
                save = true;
            //--------FPS-----
            string fps = "FPS: " + frames;
            frameCounter++;
            timeCounter += (float)gameTime.ElapsedGameTime.TotalSeconds;
            if (timeCounter > 1f)
            {
                frames = (float)frameCounter / timeCounter;
                fps += frames.ToString();
                timeCounter = 0f;
                frameCounter = 0;
            }
            //-----------------
            //spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate,
            //  SaveStateMode.None);

            
            //GraphicsDevice.Clear(Color.TransparentBlack);
            //draw all bloom-sets and store each resolving texture
            foreach (DrawSet set in drawSets)
            {
               
                currentDrawSetId = set.Id;
                set.SetGlobalBloom();
                DrawAll(gameTime);
                if(set.bloomEnabled)
                    game.bloom.Draw(gameTime);
                float bla1 = ContentStorage.screenHeight;
                float bla2 = ContentStorage.screenWidth;
                //ResolveTexture2D test = new ResolveTexture2D(GraphicsDevice, 
                GraphicsDevice.ResolveBackBuffer(set.texture);
#if !XBOX
                if (save)
                    set.texture.Save(set.Id+".jpg", ImageFileFormat.Jpg);
#endif
                GraphicsDevice.Clear(Color.TransparentBlack);
                
            }
            save = false;
            SpriteBatch.Begin(SpriteBlendMode.Additive,
                              SpriteSortMode.Immediate,
                              SaveStateMode.None);

            //draw all drawSets together
            foreach (DrawSet set in drawSets)
            {
                SpriteBatch.Draw(set.texture, new Rectangle(0, 0, ContentStorage.screenWidth, 
                    ContentStorage.screenHeight), Color.White);
            }
            
            SpriteBatch.End();

            //-----------debug-stuff
            SpriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.None);
            //SpriteBatch.DrawString(debugFont, debugString, new Vector2(100, 100), Color.White, 0f, Vector2.Zero, 1f, SpriteEffects.None, 1f);
            SpriteBatch.DrawString(debugFont, fps, new Vector2(600, 20), Color.White, 0f, Vector2.Zero, 3f, SpriteEffects.None, 1f);
            SpriteBatch.End();
            debugString = "";

            /*
            // The time since Update was called last
            //float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;

            //float fps2 = 1 / elapsed;
            //deltaFPSTime += elapsed;

            //if (deltaFPSTime > 1)
            //{
            //    memory = deltaFPSTime;
            //    deltaFPSTime -= 1;
            //}
            //spriteBatch.DrawString(debugFont, fps2.ToString(),
            //        new Vector2(20.0f, 20.0f), Color.Black);

            ////FPS-trace
            //if (++fpsCount == 100)
            //{
            //    fpsCount = 0;
            //    lowestFPS = 100;
            //}
            //averageFPS[fpsCount] = 1.0 / (gameTime.ElapsedGameTime.TotalMilliseconds / 1000.0);
            //double fps = 0;
            //for (int i = 0; i < 100; i++)
            //{
            //    fps += averageFPS[fpsCount];
            //}
            //fps /= 100;

            //if (fps < lowestFPS)
            //    lowestFPS = fps;
            //spriteBatch.DrawString(debugFont, fps.ToString(),
            //    new Vector2(20.0f, 20.0f), Color.Black);
            //spriteBatch.DrawString(debugFont, lowestFPS.ToString(),
            //    new Vector2(20.0f, 40.0f), Color.Black);

            //spriteBatch.End();
             * */
        }
        public bool paused = false;
        private double timer = 0;
        public float bloomVar = 0;
        public override void Update(GameTime gameTime)
        {
            timer += gameTime.ElapsedGameTime.TotalSeconds;
            //bloomVar = (float)Math.Sin(timer) * 0.5f + bloomBase;
            bloomVar = bloomBase+1f;
            GetDrawSet("defaultBloom").SetBloomValues(bloomVar);
            if (Input.IsNewKeyPress(Microsoft.Xna.Framework.Input.Keys.P))
                paused = !paused;
            if (!paused)
            {
                toUpdate.Clear();
                foreach (GameScreen screen in screens)
                {
                    toUpdate.Add(screen);
                }
                foreach (GameScreen screen in toUpdate)
                {
                    screen.Update(gameTime);
                }
            }
        }
        public void ExitGame()
        {
            Game.Exit();
        }
        public GameScreen GetScreen(string screenName)
        {
            if (screenName == "gameplayScreen")
                return gameplayPointer;
            foreach (GameScreen screen in totalScreens)
            {
                if (screen.ScreenName == screenName)
                    return screen;
            }
            return null;
        }
        
        #endregion
        
        #region Properties
        
        public SpriteBatch SpriteBatch
        {
            get { return spriteBatch; }
        }
        public ContentManager Content
        {
            get { return content; }
        }
        public InputState Input
        {
            get { return game.Input; }
        }

        #endregion

    }

    public class MenuTree
    {
        public GameScreen item;
        public List<MenuTree> childs;
        public MenuTree parentTree = null;

        public MenuTree(GameScreen item)
        {
            this.item = item;
            childs = new List<MenuTree>();
        }
        public string Id
        {
            get 
            {
                if (item != null)
                    return item.ScreenName;
                else
                    return "no value";
            }
        }
        public MenuTree AddItem(GameScreen screen)
        {
            MenuTree newTree = new MenuTree(screen);
            newTree.parentTree = this;
            childs.Add(newTree);
            return newTree;
        }
    }
}
